/***************************************************************************
 *   Copyright (C) 2006 by Archimedes Projekt   *
 *   thelsb@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef TMMTELEMETRY_H
#define TMMTELEMETRY_H

#include <QtDebug>
#include <QtNetwork/QTcpSocket>
#include <QThread>
#include <QTimer>
#include <QList>
#include <QApplication>
#include <QByteArray>
#include <QDataStream>
#include <QMutex>
#include "qextserialport.h"
#include "globals.h"
#include <iostream>

////////MDataReader changes (newest on top)///////
//- added setPollInterval()
//////////////////////////////////////////////////
//using namespace std;
using namespace Qt;
namespace TM {


/**
Abstract class that implements generic operations needed in order to read the TM data from the
serial port, order the data in the MDataBlock class and propagte it to the sender object.
*/

class MDataReader : public QThread
{
 Q_OBJECT
private:
 QString m_port;
 QList<MDataBlock*> *m_blockList;
protected:
 QextSerialPort *m_serial;
 QDataStream m_serialStream;
 QTimer *m_timer;
 QMutex m_mutex;
 int m_pollInterval; //in ms
private:
 /*! \fn Copy constructor (deny copying) */
   MDataReader(const MDataReader &instance) throw (MException): QThread(NULL) {}
  /*!\fn operator=() */
  const MDataReader & operator=(const MDataReader &instance) {} 
public:
   MDataReader(const QString &sPort,QObject *parent=0): QThread(parent)
   ,m_port(sPort)
   ,m_serial(NULL)
   ,m_timer(NULL)
   ,m_pollInterval(100)
   {}

   ~MDataReader()
   {
    //delete all the thread specific allocations
    if(m_serial) delete m_serial;
    //clean up the stored data blocks
    for(int i=0;i < m_blockList->size(); ++i)
     delete (*m_blockList)[i];
    delete m_blockList;
   }
   const QString & port() const { return m_port; }
   bool initSerial() throw (MException)
   {
     m_serial = new QextSerialPort(m_port);
     qDebug()<<"portName:"<<m_serial->portName();
     if(!m_serial)
     {
      qWarning("MTelemetry could not allocate serial port object") ;
      throw MException("MTelemetry could not allocate serial port object");
     } 
     else {
            m_serial->setBaudRate(BAUD57600);
            m_serial->setDataBits(DATA_8);
            m_serial->setParity(PAR_NONE);
            m_serial->setStopBits(STOP_1);
            m_serial->setFlowControl(FLOW_OFF);
            m_serial->setPortName(m_port);
            //m_serial->setTimeout(1);
            if(!m_serial->open(QIODevice::ReadOnly | QIODevice::Unbuffered))
            {
              qWarning("Telemetry receiver could not open serial port %s",qPrintable(m_serial->portName()));
              throw MException("Telemetry receiver could not open serial port"+m_serial->portName());
            }  
            else {
                  m_serialStream.setDevice(m_serial);
                  return true;
                 }

          }
      qDebug()<<"return false";
      return false;
    }
    virtual void sync()=0;
    virtual void run()
    {
     m_blockList = new QList<MDataBlock*>();
     if(initSerial())
     {
      try {
      m_timer = new QTimer(this);
      connect(m_timer,SIGNAL(timeout()),this,SLOT(readAndAppendTM()));
      qDebug()<<"serial openned";
      connect(m_serial,SIGNAL(readyRead()),this,SLOT(readAndAppendTM()));
      qDebug()<<"TMDataReader wating for data...";
      m_timer->start(m_pollInterval);
      }
      catch (MException &e)
      {
        qWarning()<<e.qwhat();
      }
      exec();
      delete m_timer;
      delete m_serial;
      delete m_blockList;
     }
     qWarning("aborting...");
     //stop thread
     quit();
     //exit application
     //::exit(0);

    }
    
    void setPollInterval(int interval) 
    {
        m_pollInterval = interval;
    }
public:   
signals:
 void propagateData(MDataBlock);
public slots:
  /** Reads the TM block from the TM source.
     This method needs to be implemented in the subclass, since each MTelemetry
     subclass has its own MDataBlock implementation */
  virtual MDataBlock * readTM() throw(MException)=0;
 void readAndAppendTM() throw(MException)
   {
     qDebug()<<"readAndAppendTM";
     try{
     m_mutex.lock();
     appendBlock(readTM());
     m_mutex.unlock();
     }
     catch(.../*MException &e*/)
     {
        qDebug()<<"exception cought ";
//        qDebug()<<"exception cought "<<e.qwhat();
//        std::cout<<e.what()<<std::endl;
     }
     
   }
public slots:   
  /** Stores TM block in intenal block list and sends a signal if successful*/
  void appendBlock(MDataBlock *dataBlock) throw(MException)
   {    
      qDebug()<<"appendBlock";
      if(dataBlock)
      {
       m_blockList->push_back(dataBlock);
       qDebug()<<"appendBlock before emit";
       MDataBlock temp(MDataBlock::CAMCON);
       qDebug()<<"after temp";
       emit propagateData( temp/**m_blockList->back()*/ );
       qDebug()<<"appendBlock after emit";
      }
   }



};



/**
Abstract telemetry class.
This class implements the generic operations needed to get TM blocks from the TM reader object and
propagte them to the data storage and view servers
*/
class MTelemetry : public QThread
{
Q_OBJECT
private:
 /*! \fn MTelemetry(const &MTelemetry)
  copy constructor - declared as private to prevent its use */
  MTelemetry(const MTelemetry &instance):QThread(NULL) { }
protected:
 MDataReader *m_reader;
 QList<MDataBlock*> *m_blockList;
 QTcpSocket *m_dataSocket,
            *m_viewSocket;
 QString m_dataServer,
         m_viewServer,
         m_sPort,
         m_logFileName;
 quint16 m_dataPort,
         m_viewPort;
 bool    m_stop;        
 QDataStream *m_dataStream,
             *m_viewStream,
             *m_logStream;
 QFile *m_fTmLog;
 
public:
  MTelemetry(QString sPort,quint16 dataPort,quint16 viewPort,QString logFileName,QString dataServer="localhost",QString viewServer="localhost",QObject *parent = 0) throw(MException): QThread(parent)
 ,m_dataSocket(NULL)
 ,m_viewSocket(NULL)
 ,m_dataServer(dataServer)
 ,m_viewServer(viewServer)
 ,m_sPort(sPort)
 ,m_logFileName(logFileName)
 ,m_dataPort(dataPort)
 ,m_viewPort(viewPort)
 ,m_stop(false)
 ,m_dataStream(NULL)
 ,m_viewStream(NULL)
 ,m_logStream(NULL)
 ,m_fTmLog(NULL)
  {  }

  /*! \fn ~MTelemetry() */
  virtual ~MTelemetry()
  {
   if(m_reader)
   {
    m_reader->exit();
    delete m_reader;
   } 
   if(m_dataSocket) delete m_dataSocket;
   if(m_viewSocket) delete m_viewSocket;
   if(m_dataStream) delete m_dataStream;
   if(m_viewStream) delete m_viewStream;
   if(m_blockList)
   {
    for(int i=0;i<m_blockList->size();++i)
     delete (*m_blockList)[i];
    delete m_blockList;
   } 
  }
  
  /*! \fn initTmReader(QString &sPort)
     Initialises the Telemetry reader object.
     This methods needs to be implemented in the implementing class,
     since each telemetry reader is different */
  virtual MDataReader * initTmReader(const QString &sPort) throw(MException)=0;

  
  /*! \fn initLogFile()
   Initilises the local telemetry log file*/
  bool initLogFile() throw(MException)
  {
   if(!m_logFileName.isEmpty()) 
    m_fTmLog = new QFile(m_logFileName);
   else throw MException("MDataReader::initLogFile(): empty logFileName");
   if(m_fTmLog->open( QIODevice::WriteOnly|QIODevice::Unbuffered))
   {
    m_logStream = new QDataStream(m_fTmLog);
    return true;
   }
   throw MException("MDataReader::initLogFile():could not open "+m_logFileName);
   return false;
  }
  
  /**Initialises the data storage and view sockets*/
  void initSockets() throw(MException)
  {
    m_dataSocket = new QTcpSocket;
    m_viewSocket = new QTcpSocket;
    m_dataStream = new QDataStream(m_dataSocket);
    m_viewStream = new QDataStream(m_viewSocket);
    if(!m_dataServer.isEmpty())
      m_dataSocket->connectToHost(m_dataServer,m_dataPort);
     connect(m_dataSocket,SIGNAL(connected()),this,SLOT(updateConnectionStatus()));
     connect(m_dataSocket,SIGNAL(disconnected()),this,SLOT(updateConnectionStatus()));
     if(!m_viewServer.isEmpty())
      m_viewSocket->connectToHost(m_viewServer,m_viewPort);
     connect(m_viewSocket,SIGNAL(connected()),this,SIGNAL(sig_connected()));
     connect(m_viewSocket,SIGNAL(disconnected()),this,SIGNAL(sig_disconnected()));
  }
  /** Returns the next block in the block list, and removes the block from the list*/
  MDataBlock* getNextBock() throw(MException)
  {
   MDataBlock *temp = m_blockList->front();
   m_blockList->pop_front();
   return temp;
  }

  /**The thread work method*/
  virtual void run()
  {
   try {
   initTmReader(m_sPort)->start();
   m_blockList = new QList<MDataBlock*>();
   initLogFile();
   initSockets();
   QTimer::singleShot(0,this,SLOT(processData()));
   connect(m_reader,SIGNAL(propagateData(MDataBlock)),this,SLOT(processData()));
   }
   catch (MException &e)
   {
    qWarning()<<e.qwhat();
   }
   exec();
  }
signals:
 void sig_connected();
 void sig_disconnected();
public slots:
  /*! \fn stop(bool on)
  sets the stop flag, if the stop flag is set to 'true' the thread stops processing data */
  void stop(bool on) {  m_stop = on; }
  /*! \fn void getReadData(MDataBlock dataBlock)
   Gets a TM block  stores it in the block list and logs it in the local telemetry log file*/
//   void getReadData(MDataBlock dataBlock)
//   {
//     MDataBlock *block = new MDataBlock(dataBlock);
//     //*block = dataBlock;
//     m_blockList->push_back(block);
//     *m_logStream<<block->byteArray();
//   }
  /*! \fn void processData()
  gets telemetry from the telemetry source and sends it to data storage and view sockets */
  void processData() throw(MException)
  {
   MDataBlock *currBlock = NULL;
   //while(!m_stop)
   //{
      if(m_blockList->size())
      currBlock = getNextBock();
       if(m_dataSocket->state() == QAbstractSocket::ConnectedState)
        *m_dataStream<<currBlock->byteArray();
       if(m_viewSocket->state() == QAbstractSocket::ConnectedState)
        *m_viewStream<<currBlock->byteArray();
      //TODO: check for write success
      //if success:
        if(currBlock)
        delete currBlock;
      //else notify  
      QApplication::processEvents() ;

   //}
  }
  
};


}; //TM namespace

#endif
